/*
 * INCLUDE FILES
 ****************************************************************************************
 */
#include <stdio.h>
#include <string.h>

#include "gap_api.h"
#include "gatt_api.h"
#include "ble_stack.h"

#include "os_timer.h"
#include "os_mem.h"
#include "app_at.h"
#include "button.h"
#include "jump_table.h"
#include "co_printf.h"
#include "co_log.h"
#include "sys_utils.h"

#include "user_task.h"
#include "prf_server.h"
#include "prf_client.h"

#include "plf.h"
#include "driver_system.h"
#include "driver_pmu.h"
#include "driver_uart.h"
#include "driver_spi.h"

#undef LOG_LOCAL_LEVEL
#define LOG_LOCAL_LEVEL        (LOG_LEVEL_INFO)
const char *app_tag = "project";


#define SYSTEM_STACK_SIZE           0x800

uint8_t slave_link_conidx;
uint8_t master_link_conidx;
uint8_t tick = 1;

__attribute__((section("stack_section"))) static uint32_t system_stack[SYSTEM_STACK_SIZE/sizeof(uint32_t)];

const struct jump_table_version_t _jump_table_version __attribute__((section("jump_table_3"))) =
{
    .stack_top_address = &system_stack[SYSTEM_STACK_SIZE/sizeof(uint32_t)],
    .firmware_version = 0x00000000,
};

const struct jump_table_image_t _jump_table_image __attribute__((section("jump_table_1"))) =
{
    .image_type = IMAGE_TYPE_APP,
    .image_size = 0x19000,
};

void proj_ble_gap_evt_func(gap_event_t *event)
{
    switch(event->type)
    {
        case GAP_EVT_ADV_END:
        {
            LOG_INFO(app_tag,"adv_end,status:0x%02x\r\n",event->param.adv_end.status);
        }
        break;
        case GAP_EVT_SCAN_END:
            LOG_INFO(app_tag,"scan_end,status:0x%02x\r\n",event->param.scan_end_status);
            break;
        case GAP_EVT_ADV_REPORT:
        {
            if(memcmp(event->param.adv_rpt->src_addr.addr.addr,"\xfd\x37\xe3\xe1\xfC\x02",6)==0)
            {
                LOG_INFO(app_tag,"evt_type:0x%02x,rssi:%d\r\n",event->param.adv_rpt->evt_type,event->param.adv_rpt->rssi);
            }

        }
        break;

        case GAP_EVT_ALL_SVC_ADDED:
        {
            LOG_INFO(app_tag,"all svc added\r\n");
        }
        break;

        case GAP_EVT_MASTER_CONNECT:
        {
            LOG_INFO(app_tag,"master[%d],connect. link_num:%d\r\n",event->param.master_connect.conidx,gap_get_connect_num());
            master_link_conidx = (event->param.master_connect.conidx);
        }
        break;

        case GAP_EVT_SLAVE_CONNECT:
        {
            LOG_INFO(app_tag,"slave[%d],connect. link_num:%d\r\n",event->param.slave_connect.conidx,gap_get_connect_num());
            slave_link_conidx = event->param.slave_connect.conidx;
            gap_conn_param_update(event->param.slave_connect.conidx, 9, 9, 10, 500);
            gap_security_req(event->param.slave_connect.conidx);
        }
        break;

        case GAP_EVT_DISCONNECT:
        {
            //gap_bond_manager_info_clr("\x0C\x0C\x0C\x0C\x0C\x0B", 0);
            LOG_INFO(app_tag,"Link[%d] disconnect,reason:0x%02X\r\n",event->param.disconnect.conidx
                      ,event->param.disconnect.reason);
            gap_start_advertising(0);
        }
        break;

        case GAP_EVT_LINK_PARAM_REJECT:
            LOG_INFO(app_tag,"Link[%d]param reject,status:0x%02x\r\n"
                      ,event->param.link_reject.conidx,event->param.link_reject.status);
            break;

        case GAP_EVT_LINK_PARAM_UPDATE:
            LOG_INFO(app_tag,"Link[%d]param update,interval:%d,latency:%d,timeout:%d\r\n",event->param.link_update.conidx
                      ,event->param.link_update.con_interval,event->param.link_update.con_latency,event->param.link_update.sup_to);
            break;

        case GAP_EVT_CONN_END:
            LOG_INFO(app_tag,"conn_end,reason:0x%02x\r\n",event->param.conn_end_reason);
            break;

        case GAP_EVT_PEER_FEATURE:
            LOG_INFO(app_tag,"peer[%d] feats ind\r\n",event->param.peer_feature.conidx);
            break;

        case GAP_EVT_MTU:
            LOG_INFO(app_tag,"mtu update,conidx=%d,mtu=%d\r\n"
                      ,event->param.mtu.conidx,event->param.mtu.value);
            break;
        case GAP_EVT_LINK_RSSI:
            LOG_INFO(app_tag,"link rssi %d\r\n",event->param.link_rssi);
            break;
        default:
            break;
    }
}
void app_rtos_entry(uint8_t type, void *arg);

/*********************************************************************
 * @fn      user_entry_before_sleep_imp
 *
 * @brief   Before system goes to sleep mode, user_entry_before_sleep_imp()
 *          will be called, MCU peripherals can be configured properly before 
 *          system goes to sleep, for example, some MCU peripherals need to be
 *          used during the system is in sleep mode. 
 *
 * @param   None. 
 *       
 *
 * @return  None.
 */
__attribute__((section("ram_code"))) void user_entry_before_sleep_imp(void)
{
    pmu_calibration_stop();

    uart_putc_noint_no_wait(UART0, 's');
    co_delay_100us(1);

    pmu_set_pin_to_PMU(GPIO_PORT_A, (1<<GPIO_BIT_0));
    pmu_set_pin_dir(GPIO_PORT_A, (1<<GPIO_BIT_0), GPIO_DIR_IN);
    pmu_set_pin_pull(GPIO_PORT_A, (1<<GPIO_BIT_0),GPIO_PULL_NONE);
}

/*********************************************************************
 * @fn      user_entry_after_sleep_imp
 *
 * @brief   After system wakes up from sleep mode, user_entry_after_sleep_imp()
 *          will be called, MCU peripherals need to be initialized again, 
 *          this can be done in user_entry_after_sleep_imp(). MCU peripherals
 *          status will not be kept during the sleep. 
 *
 * @param   None. 
 *       
 *
 * @return  None.
 */
__attribute__((section("ram_code"))) void user_entry_after_sleep_imp(void)
{
    pmu_set_pin_to_CPU(GPIO_PORT_A, (1<<GPIO_BIT_0));
    
    system_set_port_mux(GPIO_PORT_A, GPIO_BIT_0, PORTA0_FUNC_UART0_RXD);
    system_set_port_mux(GPIO_PORT_A, GPIO_BIT_1, PORTA1_FUNC_UART0_TXD);
    uart_init(UART0, 1152);
    fr_uart_enableIrq(UART0, Uart_irq_erbfi);

    /* RC calibration start. Ensure the accuracy of sleep wake time */
    pmu_calibration_start(PMU_CALI_SEL_RCLFOSC, LP_RC_CALIB_CNT);

    uart_putc_noint_no_wait(UART0, 'w');
    co_delay_100us(1);

    NVIC_EnableIRQ(PMU_IRQn);
}

os_timer_t ttt_timer;
void ttt_tim_fn(void *arg)
{
    LOG_INFO(app_tag,"ttt\r\n");
}

void proj_init(void)
{
    LOG_INFO(app_tag,"proj_init\r\n");
    LOG_INFO(app_tag,"lp clk=%d\r\n", pmu_get_rc_clk(false));

    gap_dev_name_set("FR8000", strlen("FR8000"));

    mac_addr_t addr;
    enum ble_addr_type addr_type;
    gap_address_get(&addr, &addr_type);
    LOG_INFO(app_tag, "Local BDADDR: %02X:%02X:%02X:%02X:%02X:%02X\r\n", addr.addr[5], addr.addr[4], addr.addr[3], addr.addr[2], addr.addr[1], addr.addr[0]);
    
    //gap_set_dev_name("FR8010H", strlen("FR8010H"));
    /* register GAP callback function*/
    gap_set_cb_func(proj_ble_gap_evt_func);
		/* initialize SMP */
    //gap_bond_manager_init(BLE_BONDING_INFO_SAVE_ADDR, BLE_REMOTE_SERVICE_SAVE_ADDR, 8, true);
    gap_bond_manager_init(BLE_BONDING_INFO_SAVE_ADDR,BLE_REMOTE_SERVICE_SAVE_ADDR,8,true);

    uint8_t adv_data[]="\x09\x08\x46\x52\x38\x30\x31\x30\x46\x00";
    uint8_t rsp_data[]="\x09\xFF\x00\x60\x52\x57\x2D\x42\x4C\x45";

    gap_adv_param_t adv_param;
    adv_param.adv_mode = GAP_ADV_MODE_UNDIRECT;
    adv_param.adv_addr_type = GAP_ADDR_TYPE_PRIVATE;
    adv_param.adv_chnl_map = GAP_ADV_CHAN_ALL;
    adv_param.adv_filt_policy = GAP_ADV_ALLOW_SCAN_ANY_CON_ANY;
    adv_param.adv_intv_min = 600;
    adv_param.adv_intv_max = 600;
    gap_set_advertising_param(&adv_param);
    gap_set_advertising_data(adv_data, sizeof(adv_data)-1);
    gap_set_advertising_rsp_data(rsp_data, sizeof(rsp_data)-1);
    gap_start_advertising(0);

    prf_server_create();
    prf_client_create();
		
    os_timer_init(&ttt_timer,ttt_tim_fn,NULL);
    os_timer_start(&ttt_timer,2000,1);
    
    NVIC_EnableIRQ(PMU_IRQn);
}

#include "freertos.h"
#include "timers.h"
#include "task.h"

xTimerHandle xBacklightTimer = NULL;
void vBacklightTimerCallback( xTimerHandle pxTimer )
{
    //co_printf("vBacklightTimerCallback\r\n");
    uart_putc_noint_no_wait(UART0, 'K');
    uart_putc_noint_no_wait(UART0, '\r');
    uart_putc_noint_no_wait(UART0, '\n');
}

TaskHandle_t task1_hdl;
void vTask1( void * pvParameters )
{

    proj_init();
    //vTaskDelete(task1_hdl);

    for( ;; )
    {
        LOG_INFO(app_tag,"vTask1\r\n");
        vTaskDelay(2000 / portTICK_RATE_MS);
    }
}

void vTask2( void * pvParameters )
{
    for( ;; )
    {
        LOG_INFO(app_tag,"vTask2\r\n");
        vTaskDelay(5000 / portTICK_RATE_MS);
    }
}

void user_main(void)
{
    /* initialize log module */
    log_init();
	
    /* initialize PMU module at the beginning of this program */
    pmu_sub_init();

    /* set system clock */
    system_set_clock(SYSTEM_CLOCK_SEL);

    /* set local BLE address. use the SOC unique ID as the address */
    mac_addr_t mac_addr;
    system_get_unique_ID(mac_addr.addr);
    mac_addr.addr[5] = 0x20|0xC0;
    gap_address_set(&mac_addr, BLE_ADDR_TYPE_PRIVATE);
	
    /* configure ble stack capabilities */
    ble_stack_configure(BLE_STACK_ENABLE_MESH,
                        BLE_STACK_ENABLE_CONNECTIONS,
                        BLE_STACK_RX_BUFFER_CNT,
                        BLE_STACK_RX_BUFFER_SIZE, 
                        BLE_STACK_TX_BUFFER_CNT, 
                        BLE_STACK_TX_BUFFER_SIZE, 
                        BLE_STACK_ADV_BUFFER_SIZE,
                        BLE_STACK_RETENTION_RAM_SIZE,
                        BLE_STACK_KEY_STORAGE_OFFSET);	
    //jump_table_set_static_keys_store_offset(JUMP_TABLE_STATIC_KEY_OFFSET);
    /* initialize ble stack */
    ble_stack_init();
    
    /* 
     * PA1 is used for uart tx in this sample, pull this pin to high level during system in
     * sleep mode.
     */
    //pmu_set_pin_pull(GPIO_PORT_A, (1<<GPIO_BIT_1), GPIO_PULL_UP);

    LOG_INFO(app_tag,"ble done\r\n");
		
#if 1
    //system_sleep_disable();
		system_sleep_enable();
#endif
		
    __jump_table.system_option |= SYSTEM_OPTION_ENABLE_RTOS;
    rtos_entry = app_rtos_entry;
    if(__jump_table.system_option & SYSTEM_OPTION_ENABLE_RTOS)
    {
        xBacklightTimer = xTimerCreate( "BacklightTimer",( 3000 / portTICK_RATE_MS),pdTRUE,0,vBacklightTimerCallback);
        xTimerStart( xBacklightTimer, 10 );
        static TaskHandle_t task2_hdl;
        xTaskCreate((TaskFunction_t)vTask1, "vtask1", 512, NULL, ( configMAX_PRIORITIES - 1 ), &task1_hdl);
        xTaskCreate((TaskFunction_t)vTask2, "vtask2", 512, NULL, ( configMAX_PRIORITIES - 2 ), &task2_hdl);
        rtos_entry(RTOS_ENTRY_TYPE_INIT, NULL);
        LOG_ERR(app_tag,"can't go here\r\n");
    }
}
